use crate::common::Server;
use ctapi::router::dto::device_entry::DeviceEntry;
use rstest::{fixture, rstest};
use serde_json::{Value, json};
use share::core::channel::Channel;
use share::core::device_id::DeviceId;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::Mutex;
use uuid::Uuid;

mod common;

#[fixture]
async fn shared_server() -> Arc<Mutex<Server>> {
    common::shared_server().await
}

/// 测试添加设备
#[tokio::test]
#[rstest]
async fn test_add_device(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let device_id = DeviceId::new(&Uuid::new_v4().to_string()).unwrap();

    let desc = "abc";
    server_guard.delete("/device/clear").await;
    let req = json!({
        "id": device_id,
        "desc": desc,
    });
    server_guard.post_json("/device", req.to_string()).await;

    let response = server_guard.get("/device/all").await;
    let devices: Vec<DeviceEntry> = serde_json::from_str(&response).unwrap();

    assert_eq!(devices.len(), 1);
    assert_eq!(devices[0].id, device_id);
    assert_eq!(devices[0].desc, Some(desc.to_string()));
}

/// 测试删除设备
#[tokio::test]
#[rstest]
async fn test_delete_device(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let id0 = DeviceId::new(&Uuid::new_v4().to_string()).unwrap();
    server_guard.delete("/device/clear").await;
    let req0 = json!({
        "id": id0,
        "desc": "abc",
    });
    let id1 = DeviceId::new(&Uuid::new_v4().to_string()).unwrap();
    let req1 = json!({
        "id": id1,
        "desc": "def",
    });
    server_guard.post_json("/device", req0.to_string()).await;
    server_guard.post_json("/device", req1.to_string()).await;
    server_guard
        .delete(format!("/device/{}", id0.to_string()))
        .await;

    let response = server_guard.get("/device/all").await;
    let devices: Vec<DeviceEntry> = serde_json::from_str(&response).unwrap();

    assert_eq!(devices.len(), 1);
    assert_eq!(devices[0].id, id1);
    assert_eq!(devices[0].desc, Some("def".to_string()));
}

/// 测试清空所有设备
#[tokio::test]
#[rstest]
async fn test_clear_devices(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    server_guard.delete("/device/clear").await;
    let req0 = json!({
        "id": Uuid::new_v4(),
        "desc": "abc",
    });
    let req1 = json!({
        "id": Uuid::new_v4(),
        "desc": "def",
    });
    server_guard.post_json("/device", req0.to_string()).await;
    server_guard.post_json("/device", req1.to_string()).await;
    server_guard.delete("/device/clear").await;

    let response = server_guard.get("/device/all").await;
    let devices: Vec<DeviceEntry> = serde_json::from_str(&response).unwrap();

    assert_eq!(devices.len(), 0);
}

/// 测试添加候选设备
#[tokio::test]
#[rstest]
async fn test_add_candidates(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let my_ip = server_guard.get("/ip").await;
    let my_ip = IpAddr::from_str(&my_ip).unwrap();
    post_inner_ip(&*server_guard, my_ip).await;
    server_guard.delete("/device/candidate/clear").await;

    let c0 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    let c1 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    let c2 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    server_guard
        .post_json("/device/candidate", c0.to_string())
        .await;
    server_guard
        .post_json("/device/candidate", c1.to_string())
        .await;

    server_guard.delete("/inner_ip").await;

    server_guard
        .post_json("/device/candidate", c2.to_string())
        .await;

    let rsp = server_guard.get("/device/candidate/all").await;
    let rsp: Value = serde_json::from_str(&rsp).unwrap();
    let candidates = &rsp["candidates"].as_array().unwrap();
    assert_eq!(candidates.len(), 2);

    // 检查返回的candidates是否包含c0和c1（不考虑顺序）
    let has_c0 = candidates.iter().any(|c| {
        c["custom_device_id"] == c0["custom_device_id"]
            && c["device_id"] == c0["device_id"]
            && c["channel"] == c0["channel"]
    });

    let has_c1 = candidates.iter().any(|c| {
        c["custom_device_id"] == c1["custom_device_id"]
            && c["device_id"] == c1["device_id"]
            && c["channel"] == c1["channel"]
    });

    // 确保两个预期元素都存在
    assert!(has_c0, "candidates should contain c0");
    assert!(has_c1, "candidates should contain c1");
}

/// 测试在没有设置内网IP地址时添加候选设备
///
/// 服务器只允许内网IP的请求才可以添加候选设备
#[tokio::test]
#[rstest]
async fn test_add_candidates_when_no_inner_ip_should_fail(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    server_guard.delete("/inner_ip").await;
    server_guard.delete("/device/candidate/clear").await;

    let c0 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    let c1 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    let c2 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    server_guard
        .post_json("/device/candidate", c0.to_string())
        .await;
    server_guard
        .post_json("/device/candidate", c1.to_string())
        .await;
    server_guard
        .post_json("/device/candidate", c2.to_string())
        .await;

    let rsp = server_guard.get("/device/candidate/all").await;
    let rsp: Value = serde_json::from_str(&rsp).unwrap();
    let candidates = &rsp["candidates"].as_array().unwrap();
    assert_eq!(candidates.len(), 0);
}

/// 测试当请求的IP地址不是内网IP时添加候选设备
///
/// 服务器只允许内网IP的请求才可以添加候选设备
#[tokio::test]
#[rstest]
async fn test_add_candidates_when_inner_ip_not_match_should_fail(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let another_ip = IpAddr::from_str("2.2.2.2").unwrap();
    post_inner_ip(&*server_guard, another_ip).await;
    server_guard.delete("/device/candidate/clear").await;

    let c0 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    let c1 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    let c2 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    server_guard
        .post_json("/device/candidate", c0.to_string())
        .await;
    server_guard
        .post_json("/device/candidate", c1.to_string())
        .await;
    server_guard
        .post_json("/device/candidate", c2.to_string())
        .await;

    let rsp = server_guard.get("/device/candidate/all").await;
    let rsp: Value = serde_json::from_str(&rsp).unwrap();
    let candidates = &rsp["candidates"].as_array().unwrap();
    assert_eq!(candidates.len(), 0);
}

/// 测试清空候选设备
#[tokio::test]
#[rstest]
async fn test_clear_candidates(
    #[future] shared_server: Arc<Mutex<Server>>
) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let my_ip = server_guard.get("/ip").await;
    let my_ip = IpAddr::from_str(&my_ip).unwrap();
    post_inner_ip(&*server_guard, my_ip).await;
    server_guard.delete("/device/candidate/clear").await;

    let c0 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    let c1 = json!({
        "custom_device_id": Uuid::new_v4(),
        "device_id": Uuid::new_v4(),
        "channel": Channel::ForceLogin,
    });
    server_guard
        .post_json("/device/candidate", c0.to_string())
        .await;
    server_guard
        .post_json("/device/candidate", c1.to_string())
        .await;

    server_guard.delete("/device/candidate/clear").await;

    let rsp = server_guard.get("/device/candidate/all").await;
    let rsp: Value = serde_json::from_str(&rsp).unwrap();
    let candidates = &rsp["candidates"].as_array().unwrap();
    assert_eq!(candidates.len(), 0);
}

async fn post_inner_ip(mut server: &Server, ip: IpAddr) {
    let rsp = reqwest::Client::new()
        .post(server.url("/inner_ip"))
        .header("X-Real-Ip", ip.to_string())
        .send()
        .await
        .expect("Failed to post inner ip");

    if rsp.status() != reqwest::StatusCode::OK {
        panic!(
            "Failed to post inner ip, the return code is {}",
            rsp.status()
        );
    }
}
async fn post_candidate(mut server: &Server, ip: IpAddr) {
    let rsp = reqwest::Client::new()
        .post(server.url("/device/candidate"))
        .send()
        .await
        .expect("Failed to post inner ip");

    if rsp.status() != reqwest::StatusCode::OK {
        panic!(
            "Failed to post inner ip, the return code is {}",
            rsp.status()
        );
    }
}
